#include"Buffer.h"
#include"Logger.h"
#include"Endian.h"
#include"SocketsOps.h"
#include"Config.h"
#include"TimeHelper.h"
#include<assert.h>
#include<algorithm>
using namespace Log;
namespace wjc {
	using namespace net;
	Buffer::Buffer(int id) :
		id_(id),
		buffer_(new Node(Config::BufferSizeLocal)),
		nodeSize_(1),
		totalSize_(Config::BufferSizeLocal),
		writerIndex_(0),
		readerIndex_(0)
	{
		auto last = buffer_;
		while (last->next != nullptr) last = last->next;
		last->next = buffer_;
		buffer_->prev = last;
	}
	Buffer::~Buffer() {
		auto node = buffer_;
		node->prev->next.reset();
		while (node) {
			node->prev.reset();
			node = node->next;
		}
	}

	std::string Buffer::read() {
		return read(readableBytes());
	}
	std::string Buffer::read(int len) {
		assert(len <= readableBytes());
		//len = checkReadable(len);
		int oldLen = len;
		std::string res = "";
		auto buffer = buffer_;
		int readerIndex = readerIndex_;
		while (readerIndex > buffer->size) {
			readerIndex -= buffer->size;
			buffer = buffer->next;
		}
		while (len > 0) {
			int size = std::min(static_cast<int>(buffer->size- readerIndex), len);
			res.append(buffer->data + readerIndex, size);
			if (size == (buffer->size - readerIndex))
				buffer = buffer->next;
			readerIndex = 0;
			len -= size;
		}
		retrieve(oldLen);
		return res;
	}

	int Buffer::read(char* start, char* end) {
		auto buffer = buffer_;
		int readerIndex = readerIndex_;
		while (readerIndex > buffer->size) {
			readerIndex -= buffer->size;
			buffer = buffer->next;
		}
		int len = std::min(readableBytes(), static_cast<int>(end - start));
		int old_len = len;
		while (len>0) {
			int size = std::min(static_cast<int>(buffer->size - readerIndex), len);
			memcpy(start, buffer->data + readerIndex, size);
			if (size == (buffer->size - readerIndex))
				buffer = buffer->next;
			readerIndex = 0;
			start += size;
			len -= size;
		}
		retrieve(old_len);
		return old_len;
	}

	void Buffer::peek(char* data, int len)const {
		int oldLen = len;
		auto buffer = buffer_;
		int readerIndex = readerIndex_;
		while (readerIndex > buffer->size) {
			readerIndex -= buffer->size;
			buffer = buffer->next;
		}
		while (len > 0) {
			int size = std::min(static_cast<int>(buffer->size - readerIndex), len);
			memcpy(data, buffer->data + readerIndex, size);
			if (size == (buffer->size - readerIndex))
				buffer = buffer->next;
			readerIndex = 0;
			len -= size;
			data += size;
		}
	}

	bool Buffer::write(const std::string& str) {
		return write(str.data(), str.length());
	}
	bool Buffer::write(const char* data, int len) {
		if (!ensureWritableBytes(len))
			return false;  //???
		int lenOld = len;
		auto buffer = buffer_;
		int writerIndex = writerIndex_;
		while (writerIndex > buffer->size) {
			writerIndex -= buffer->size;
			buffer = buffer->next;
		}
		while (len > 0) {
			int size = std::min(len, static_cast<int>(buffer->size - writerIndex));
			memcpy(buffer->data + writerIndex, data, size);
			if(size== (buffer->size - writerIndex))
				buffer = buffer->next;
			writerIndex = 0;
			len -= size;
			data += size;
		}
		writerIndex_ += lenOld;
		return true;
	}

	bool Buffer::ensureWritableBytes(int len) {
		int remain = writableBytes();
		if (remain < len) {
			return maskSpace(len - remain);
		}
		return true;
	}

	bool Buffer::maskSpace(int len) {
		std::shared_ptr<Node>newNode = std::make_shared<Node>(len);
		//auto newNode = BufferPool::getInstance()->getNode(id_, len);
		if (!newNode) return false;
		std::shared_ptr<Node> head = newNode;
		std::shared_ptr<Node> last1 = newNode;
		while (head) {
			++nodeSize_;
			totalSize_ += head->size;
			last1 = head;
			head = head->next;
		}
		std::shared_ptr<Node> last2 = buffer_->prev;

		last2->next = newNode;
		newNode->prev = last2;
		last1->next = buffer_;
		buffer_->prev = last1;
		return true;
	}


	bool append(char* data, int len);
	bool prepend(char* data, int len);


	void Buffer::retrieve(int len) {
		//len = checkReadable(len);
		int rdbts = readableBytes();
		assert(len <= rdbts);
		readerIndex_ += len;
		ReIndex();
	}

	struct iovec* Buffer::getIovec(int from, int to, int& size) {
		auto bufferStart = buffer_;
		while (from >= bufferStart->size) {
			from -= bufferStart->size;
			to -= bufferStart->size;
			bufferStart = bufferStart->next;
		}
		auto bufferEnd = bufferStart;
		int nodeSize = 1;
		while (to > bufferEnd->size) {
			to -= bufferEnd->size;
			bufferEnd = bufferEnd->next;
			++nodeSize;
		}
		struct iovec* vec = new struct iovec[nodeSize];
		auto buffer = bufferStart;
		for (int i = 0; i < nodeSize; ++i) {
			char* begin, * end;
			if (i == 0)
				begin = buffer->data + from;
			else
				begin = buffer->data;
			if (i == nodeSize - 1)
				end = buffer->data + to;
			else
				end = buffer->data + buffer->size;
			vec[i].iov_base = begin;
			vec[i].iov_len = end - begin;
			buffer = buffer->next;
		}
		size = nodeSize;
		return vec;
	}


	int Buffer::readFromFd(int fd, int* savedErrno)
	{
		if (writableBytes() < totalSize_ / 3)
			maskSpace(totalSize_ / 2 - writableBytes());//??
		int nodeSize = 1;
		struct iovec* vec = getIovec(writerIndex_, totalSize_, nodeSize);
		int writable = writableBytes();
		buffer_->data;
		const ssize_t n = readv(fd, vec, nodeSize);
		if (n < 0)
		{
			*savedErrno = errno;
		}
		else if (n <= writable)
		{
			writerIndex_ += n;
			if (n == writable)
				maskSpace(1);  //??
		}

		delete[] vec;
		return n;
	}

	int Buffer::writeToFd(int fd, int* savedErrno) {
		if (readableBytes() < 1) return 0;
		int nodeSize = 1;
		struct iovec* vec = getIovec(readerIndex_, writerIndex_, nodeSize);
		const ssize_t n = writev(fd, vec, nodeSize);
		if (n < 0)
		{
			*savedErrno = errno;
		}
		else {
			retrieve(n);
		}
		delete[] vec;
		return n;
	}

	void Buffer::ReIndex() {
		while (readerIndex_ >= buffer_->size) {
			readerIndex_ -= buffer_->size;
			writerIndex_ -= buffer_->size;
			buffer_ = buffer_->next;
		}
	}

	bool Buffer::writeInt32(int32_t x)
	{
		int32_t be32 = sockets::hostToNetwork32(x);
		return write(static_cast<char*>(static_cast<void*>(&be32)), sizeof(be32));
	}

	int32_t Buffer::peekInt32() const
	{
		assert((readableBytes() >=sizeof(int32_t)));
		int32_t be32 = 0;
		char* data = new char[sizeof(int32_t)];
		peek(data, sizeof(int32_t));
		::memcpy(&be32, data, sizeof be32);
		delete[] data;
		return sockets::networkToHost32(be32);
	}

}